<script setup lang="ts">
import { onMounted, onUnmounted, ref, shallowRef } from 'vue'
import {
  AxesHelper,
  Scene,
  PerspectiveCamera,
  WebGLRenderer,
  MeshStandardMaterial,
  Mesh,
  PlaneGeometry,
  MeshBasicMaterial,
  ShaderMaterial,
  RawShaderMaterial,
  DoubleSide,
  Clock,
  TextureLoader,
  EquirectangularReflectionMapping,
  sRGBEncoding,
  ACESFilmicToneMapping,
} from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { GUI } from 'dat.gui'
import Stats from 'three/examples/jsm/libs/stats.module'
import planeFrag from './plane.frag?raw'
import planeVert from './plane.vert?raw'
import envImg from './images/2k.hdr?url'
import flyLight from './model/flyLight.glb?url'
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { gsap } from 'gsap'

const contentRef = ref<HTMLCanvasElement>()
const renderer = shallowRef<WebGLRenderer>(new WebGLRenderer())
const orbitControls = shallowRef<OrbitControls>()
const scene = new Scene()
const camera = shallowRef<PerspectiveCamera>()
const axesHelper = new AxesHelper(20)
const clock = new Clock()
// 创建性能监视器
let stats = new Stats()

const planeShaderMaterial = new ShaderMaterial({
  vertexShader: planeVert,
  fragmentShader: planeFrag,
  uniforms: {
    uElapsedTime: {
      value: 0,
    },
  },
  // wireframe: true,
  side: DoubleSide,
})

const gui = new GUI()

const init = () => {
  if (contentRef.value) {
    camera.value = new PerspectiveCamera(75, contentRef.value.clientWidth / contentRef.value.clientHeight)
    camera.value.position.set(0, -2, 5)
    scene.add(camera.value)
    // scene.add(axesHelper)

    renderer.value.outputEncoding = sRGBEncoding
    renderer.value.toneMapping = ACESFilmicToneMapping
    renderer.value.toneMappingExposure = 0.2

    contentRef.value.appendChild(renderer.value.domElement)

    stats.dom.style.position = 'absolute'
    stats.dom.style.top = '0'
    stats.dom.style.left = '0'
    contentRef.value.appendChild(stats.dom)

    renderer.value.setSize(contentRef.value?.clientWidth, contentRef.value?.clientHeight)

    orbitControls.value = new OrbitControls(camera.value, renderer.value.domElement)
    orbitControls.value.enableDamping = true
  }
}

const render = () => {
  const elapsedTime = clock.getElapsedTime()
  planeShaderMaterial.uniforms.uElapsedTime.value = elapsedTime

  if (camera.value) {
    orbitControls.value?.update()
    renderer.value?.render(scene, camera.value)
  }
  stats.update()
  requestAnimationFrame(render)
}

window.addEventListener('resize', () => {
  if (contentRef.value && camera.value) {
    camera.value.aspect = contentRef.value.clientWidth / contentRef.value.clientHeight
    camera.value.updateProjectionMatrix()

    renderer.value.setSize(contentRef.value?.clientWidth, contentRef.value?.clientHeight)
    renderer.value.setPixelRatio(window.devicePixelRatio)
  }
})

onMounted(() => {
  init()
  // 加载环境贴图
  const rgbeLoader = new RGBELoader()
  rgbeLoader.loadAsync(envImg).then((texture) => {
    texture.mapping = EquirectangularReflectionMapping
    scene.background = texture
    scene.environment = texture
  })

  const gltfLoader = new GLTFLoader()
  gltfLoader.loadAsync(flyLight).then((model) => {
    const flyLightGroup = model.scene
    const lightWrapper = flyLightGroup.children[0] as Mesh
    lightWrapper.material = planeShaderMaterial

    for (let i = 0; i < 200; i++) {
      const copyFlyLight = flyLightGroup.clone(true)
      copyFlyLight.position.x = (Math.random() - 0.5) * 300
      copyFlyLight.position.z = (Math.random() - 0.5) * 300
      copyFlyLight.position.y = Math.random() * 70

      gsap.to(copyFlyLight.position, {
        x: '+=' + (Math.random() - 0.5) * 20,
        z: '+=' + (Math.random() - 0.5) * 20,
        y: '+=' + Math.random() * 40,
        duration: 10 + Math.random() * 10,
        yoyo: true,
        repeat: -1,
      })

      gsap.to(copyFlyLight.rotation, { y: Math.PI * 2, duration: 10 + Math.random() * 10, yoyo: true, repeat: -1 })
      scene.add(copyFlyLight)
    }
  })
  render()
})

onUnmounted(() => {
  gui.destroy()
})
</script>

<template>
  <div class="kmd" ref="contentRef"></div>
</template>

<style lang="scss" scoped>
.kmd {
  width: 100%;
  height: 100%;
  position: relative;
}
</style>
